package jinyilw.common.file;

import jinyilw.common.ExecuteTools;
import jinyilw.common.OSTools;
import jinyilw.common.TextTools;
import jinyilw.common.log.LogTools;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class FileTools
{
	private static final int EOF = -1;
	private static final int BUFFER_SIZE = 8192;
	public static final char separatorChar = File.separatorChar;
	public static final String separator = File.separator;

	private static String appPath;
	private static String userHomePath;
	private static String documentsPath;
	private static String desktopPath;
	private static File userHomeFolder;
	private static File userDocuments;
	private static File userDesktop;

	private static final File userFolder = new File(System.getProperty("user.dir"));

	static
	{
		LogTools.info("userFolder:" + userFolder.getAbsolutePath());
		if (OSTools.isWindows())
			LogTools.info("userFolder canWrite:" + canWrite(userFolder));
	}

	private FileTools()
	{
	}

	public static String getUserHomePath()
	{
		if (userHomePath == null)
			userHomePath = System.getProperty("user.home");
		return userHomePath;
	}

	public static File getUserHomeFolder()
	{
		if (userHomeFolder == null)
			userHomeFolder = new File(getUserHomePath());
		return userHomeFolder;
	}

	public static String getAppPath()
	{
		if (appPath == null)
			appPath = ClassLoader.getSystemResource("").getPath();
		return appPath;
	}

	public static String getDocumentsPath()
	{
		if (documentsPath == null)
			documentsPath = getUserHomePath() + separator + "Documents";
		return documentsPath;
	}

	public static String getDesktopPath()
	{
		if (desktopPath == null)
			desktopPath = getUserHomePath() + separator + "Desktop";
		return desktopPath;
	}

	public static File getUserDocuments()
	{
		if (userDocuments == null)
			userDocuments = new File(getUserHomeFolder(), "Documents");
		return userDocuments;
	}

	public static File getUserDesktop()
	{
		if (userDesktop == null)
			userDesktop = new File(getUserHomeFolder(), "Desktop");
		return userDesktop;
	}

	public static File getUserFolder()
	{
		return userFolder;
	}

	public static File getUserFile(String path)
	{
		if (path == null)
			return null;
		return new File(userFolder, path);
	}

	public static boolean deleteFolder(String dirPath)
	{
		return deleteFolder(getUserFile(dirPath));
	}

	public static boolean deleteFolder(File dirFile)
	{
		if (dirFile == null)
			return false;
		if (dirFile.isDirectory())
		{
			var files = dirFile.listFiles();
			if (files != null)
				for (File file : files)
				{
					if (file.isDirectory())
						deleteFolder(file);
					else
						file.delete();
				}
		}
		return dirFile.delete();
	}

	public static File getRandomFileInDirectory(String path)
	{
		return getRandomFileInDirectory(getUserFile(path));
	}

	public static File getRandomFileInDirectory(File directory)
	{
		if (directory == null)
			return null;
		File[] files = directory.listFiles();
		if (files == null || files.length < 1)
			return null;
		int randomI = (int) (Math.random() * files.length);
		return files[randomI];
	}

	public static List<File> getAllTypeFile(String path, String type)
	{
		return getAllTypeFile(getUserFile(path), type);
	}

	public static List<File> getAllTypeFile(File baseFile, String type)
	{
		List<File> list = new ArrayList<>();
		if (!baseFile.exists())
			return list;
		if (baseFile.isDirectory())
		{
			File[] files = baseFile.listFiles();
			if (files != null)
				for (File file : files)
				{
					if (file.isDirectory())
					{
						list.addAll(getAllTypeFile(file, type));
					} else if (file.getName().endsWith(type))
						list.add(file);
				}
		} else if (baseFile.getName().endsWith(type))
			list.add(baseFile);
		return list;
	}

	public static void copyFile(File src, File des)
	{
		if (src == null || des == null)
			return;
		if (!src.exists())
		{
			System.err.println(
					"FileTools.copyFile: 文件不存在：" + src.getAbsolutePath());
			return;
		}
		if (src.isDirectory())
		{
			des.mkdirs();
			return;
		}
		File directory = getParentFile(des);
		if (!directory.exists())
			directory.mkdirs();
		try
		{
			copy(src, des);
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public static void copyFile(String srcPath, String desPath)
	{
		File srcFile = getUserFile(srcPath);
		if (!srcFile.exists())
		{
			System.err.println("FileTools.copyFile: 文件不存在: " + srcPath);
			return;
		}
		File desFile = new File(desPath);
		if (srcFile.isDirectory())
		{
			desFile.mkdirs();
			return;
		}
		desPath = desFile.getAbsolutePath();
		File directory = new File(
				desPath.substring(0, desPath.lastIndexOf(separatorChar)));
		if (!directory.exists())
			directory.mkdirs();
		try
		{
			copy(srcFile, desFile);
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public static void copyFolder(File src, File des)
	{
		if (src == null || des == null)
			return;
		if (!src.exists())
		{
			System.err.println(
					"FileTools.copyFolder: 文件不存在：" + src.getAbsolutePath());
			return;
		}
		if (!src.isDirectory())
		{
			copyFile(src, des);
			return;
		}
		des.mkdirs();
		try
		{
			var files = src.listFiles();
			if (files != null)
				for (File f : files)
					if (f.isDirectory())
						copyFolder(f, new File(des, f.getName()));
					else
						copy(f, new File(des, f.getName()));
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	private static void copy(File src, File des) throws IOException
	{
		FileInputStream fis = new FileInputStream(src);
		FileOutputStream fos = new FileOutputStream(des);
		byte[] buffer = new byte[BUFFER_SIZE];
		int len;
		while (EOF != (len = fis.read(buffer)))
			fos.write(buffer, 0, len);
		fis.close();
		fos.close();
	}

	public static void copyInputStreamToFile(InputStream source,
			File destination) throws IOException
	{
		try (InputStream in = source;
			 OutputStream out = new FileOutputStream(destination))
		{
			copyLarge(in, out, new byte[BUFFER_SIZE]);
		}
	}

	public static long copyLarge(InputStream input, OutputStream output,
			byte[] buffer) throws IOException
	{
		long count = 0;
		int n;
		while (EOF != (n = input.read(buffer)))
		{
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

	public static byte[] getBytes(File file)
	{
		if (file == null || !file.isFile())
			return null;
		byte[] bytes = new byte[(int) file.length()];
		try (FileInputStream fis = new FileInputStream(file))
		{
			fis.read(bytes);
			return bytes;
		} catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public static void setBytes(File file, byte[] bytes)
	{
		if (file == null || bytes == null)
			return;
		try (FileOutputStream fos = new FileOutputStream(file))
		{
			fos.write(bytes);
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public static boolean move(File src, File des)
	{
		if (src == null || des == null)
			return false;
		if (!src.exists())
		{
			System.err
					.println("FileTools.move: 文件不存在：" + src.getAbsolutePath());
			return false;
		}
		boolean bool = src.renameTo(des);
		if (!bool)
			System.out
					.println("FileTools.move: 移动文件失败：" + src.getAbsolutePath());
		return bool;
	}

	public static void moveToFolder(File folder, File... files)
	{
		if (folder == null || files == null)
			return;
		for (File file : files)
		{
			file.renameTo(new File(folder, file.getName()));
		}
	}

	public static File renameTo(File src, String name)
	{
		if (src == null || TextTools.isBlank(name))
			return src;
		if (!src.exists())
		{
			System.err.println(
					"FileTools.renameTo: 文件不存在：" + src.getAbsolutePath());
		} else
		{
			File newFile = new File(getParentFile(src), name);
			if (src.renameTo(newFile))
				src = newFile;
			else
				System.out.println(
						"FileTools.renameTo: 重命名文件失败：" + src.getAbsolutePath());
		}
		return src;
	}

	public static List<String> getAllFilePath(String directoryPath,
			boolean isAddDirectory)
	{
		List<String> list = new ArrayList<>();
		File baseFile = new File(directoryPath);
		if (baseFile.isFile() || !baseFile.exists())
			return list;
		File[] files = baseFile.listFiles();
		if (files != null)
			for (File file : files)
			{
				if (file.isDirectory())
				{
					if (isAddDirectory)
						list.add(file.getAbsolutePath());
					list.addAll(
							getAllFilePath(file.getAbsolutePath(), isAddDirectory));
				} else
					list.add(file.getAbsolutePath());
			}
		return list;
	}

	public static List<String> getAllFileRelativePath(String directory)
	{
		List<String> list = getAllFilePath(new File(directory));
		int len = directory.length(), size = list.size();
		for (int i = 0; i < size; i++)
			list.set(i, list.get(i).substring(len));
		return list;
	}

	public static List<String> getAllFilePath(File baseFile)
	{
		List<String> list = new ArrayList<>();
		if (baseFile == null || !baseFile.exists())
			return list;
		if (baseFile.isDirectory())
		{
			File[] files = baseFile.listFiles();
			if (files != null)
				for (File file : files)
				{
					if (file.isDirectory())
					{
						list.addAll(getAllFilePath(file));
					} else
						list.add(file.getAbsolutePath());
				}
		} else
			list.add(baseFile.getAbsolutePath());
		return list;
	}

	public static List<File> getAllFiles(File baseFile)
	{
		List<File> list = new ArrayList<>();
		if (baseFile == null || !baseFile.exists())
			return list;
		if (baseFile.isDirectory())
		{
			File[] files = baseFile.listFiles();
			if (files != null)
				for (File file : files)
				{
					if (file.isDirectory())
					{
						list.add(file);
						list.addAll(getAllFiles(file));
					} else
						list.add(file);
				}
		} else
			list.add(baseFile);
		return list;
	}

	public static void updateFiles(String path, String updatePath,
			String newPath)
	{
		List<String> list = getAllFileRelativePath(path);
		for (String str : list)
		{
			copyFile(updatePath + str, newPath + str);
		}
	}

	public static File setToHidden(File file)
	{
		if (file == null)
			return null;
		if (file.isHidden())
		{
			return file;
		}
		if (OSTools.isWindows())
		{
			String sets = "attrib +H \"" + file.getAbsolutePath() + "\"";
			ExecuteTools.exec(sets);
		} else if (OSTools.isMac())
		{
			String sets = "chflags hidden " + file.getAbsolutePath();
			ExecuteTools.exec(sets);
		}
		return file;
	}

	public static File getHiddenFile(String path)
	{
		if (path == null)
			return null;
		return setToHidden(getUserFile(path));
	}

	public static void createParentDirectory(File file)
	{
		if (file == null || file.exists())
			return;
		getParentFile(file).mkdirs();
	}

	public static void createHiddenParentDirectory(File file)
	{
		if (file == null || file.exists())
			return;
		File parent = getParentFile(file);
		parent.mkdirs();
		setToHidden(parent);
	}

	public static File createDirectory(File directory)
	{
		if (directory == null)
			return null;
		directory.mkdirs();
		return directory;
	}

	public static File createHiddenDirectory(File directory)
	{
		if (directory == null)
			return null;
		directory.mkdirs();
		return setToHidden(directory);
	}

	public static File createDirectory(String directoryPath)
	{
		if (directoryPath == null)
			return null;
		return createDirectory(getUserFile(directoryPath));
	}

	public static File createDirectory(File parent, String path)
	{
		if (path == null)
			return null;
		return createDirectory(new File(parent, path));
	}

	public static File createHiddenDirectory(String directoryPath)
	{
		return createHiddenDirectory(getUserFile(directoryPath));
	}

	public static File createFile(File file)
	{
		if (file == null || file.exists())
			return file;
		File parent = getParentFile(file);
		if (parent != null)
			parent.mkdirs();
		return file;
	}

	public static File createFile(String filePath)
	{
		return createFile(getUserFile(filePath));
	}

	public static File getParentFile(File file)
	{
		if (file == null)
			return null;
		file.getAbsolutePath();
		return file.getParentFile();
	}

	public static boolean canRead(File file)
	{
		if (file == null)
			return false;
		if (file.isDirectory())
		{
			try
			{
				File[] listFiles = file.listFiles();
				return listFiles != null;
			} catch (Exception e)
			{
				return false;
			}
		} else if (!file.exists())
			return false;
		return checkRead(file);
	}

	private static boolean checkRead(File file)
	{
		try (FileReader fd = new FileReader(file))
		{
			fd.read();
			return true;
		} catch (IOException e)
		{
			return false;
		}
	}

	public static boolean canWrite(File file)
	{
		if (file == null)
			return false;
		if (!file.isDirectory())
			file = getParentFile(file);
		File tempFile = new File(file, ".canWriteTest.temp");
		if (tempFile.exists())
			return tempFile.delete();
		if (!file.exists())
			if (!file.mkdirs())
				return false;
		try
		{
			boolean canCreate = tempFile.createNewFile();
			if (canCreate)
				setToHidden(tempFile);
			return canCreate;
		} catch (IOException e)
		{
			// e.printStackTrace();
			return false;
		}
	}

	/**
	 * 删除文件，如果要删除的对象是文件夹，先删除所有子文件(夹)，再删除该文件
	 */
	public static boolean deleteFile(File file)
	{
		return deleteFile(file, true);
	}

	/**
	 * 删除文件，如果要删除的对象是文件夹，则根据delDir判断是否同时删除文件夹
	 *
	 * @param file   要删除的文件对象
	 * @param delDir 是否删除目录
	 * @return 删除是否成功
	 */
	public static boolean deleteFile(File file, boolean delDir)
	{
		if (!file.exists())
			return true;
		if (file.isFile())
			return file.delete();
		else
		{
			boolean result = true;
			File[] children = file.listFiles();
			if (children != null)
				for (File child : children)
				{
					result = deleteFile(child, delDir);// 递归删除文件
					if (!result)
						return false;
				}
			if (delDir)
				result = file.delete(); // 删除当前文件夹
			return result;
		}
	}

	public static InputStream getInputStream(Class<?> loadClass, String path)
	{
		if (loadClass == null)
			return null;
		if (path == null)
			return null;
		try
		{
			return loadClass.getResourceAsStream(path);
		} catch (Exception e)
		{
			LogTools.debug("FileTools.getInputStream: 获取文件错误！" + path, e);
			return null;
		}
	}
}
